package com.zengdada.authority.utils.excel.export.title;

import com.zengdada.authority.utils.CommonUtils;
import com.zengdada.authority.utils.excel.export.style.MyCellStyle;
import com.zengdada.authority.utils.excel.export.style.MyCellStyleSize;
import com.zengdada.authority.utils.excel.export.style.MyCellUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellUtil;

import java.util.*;
import java.awt.*;
import java.util.List;

/**
 * @author 12614
 * @contact 1261438511@qq.com
 * @date 2020-05-25 23:31
 * @describe excel 所有行表头处理类
 */
public class AllTableTitle implements Iterable<RowTableTitle> {

    // 行模式 0 紧凑 1 宽松 （宽松全部以最高的高度 紧凑取均值四舍五入） 3 固定高度 有重叠时右移动
    public final static int COMPACT_MODEL = 0, WIDE_MODEL = 1, FIXED_MOVE_RIGHT = 3;
    private int startRowNum = 0;
    private int startCellNum = 0;
    private int model;
    private RowTableTitle[] rowTableTitles;
    private int version = 0;
    private MyCellStyleSize cellSize;
    private MyCellStyle cellStyle;

    public MyCellStyleSize getCellSize() {
        return cellSize;
    }

    public AllTableTitle setCellSize(MyCellStyleSize cellSize) {
        this.cellSize = cellSize;
        return this;
    }

    public MyCellStyle getCellStyle() {
        return cellStyle;
    }

    public AllTableTitle setCellStyle(MyCellStyle cellStyle) {
        this.cellStyle = cellStyle;
        return this;
    }

    public int getModel() {
        return model;
    }

    public void setModel(int model) {
        this.model = model;
    }


    /**
     * // 必须添加完整的数组 （数组内容对象不能为空）
     *
     * @param model          表头创建模式
     * @param rowTableTitles 行表头数组
     * @author 12614
     * @date 2020-05-24 1:22
     */
    public AllTableTitle(int model, RowTableTitle[] rowTableTitles) {
        this(0, 0, model, rowTableTitles);
    }

    public AllTableTitle(int startRowNum, int startCellNum, int model, RowTableTitle[] rowTableTitles) {
        this.startRowNum = startRowNum;
        this.startCellNum = startCellNum;
        this.model = model;
        setRowTableTitles(rowTableTitles);
    }

    public void setRowTableTitles(RowTableTitle[] rowTableTitles) {
        if (rowTableTitles != null) {
            check(rowTableTitles);
        }
        this.rowTableTitles = rowTableTitles;
        version++;
    }

    private void check(RowTableTitle[] rowTableTitles) {
        for (RowTableTitle rowTableTitle : rowTableTitles) {
            if (rowTableTitle == null) {
                throw new NullPointerException("数组内容对象不能为空");
            }
        }
    }

    /**
     * // 表头预计算 主要更具配置 计算表格高度、位置
     *
     * @author 12614
     * @date 2020-05-27 23:19
     */
    private void calculation() {
        // 计算行高 及位置
        for (RowTableTitle rowTableTitle : this.rowTableTitles) {
            rowTableTitle.calculation();
        }
        if (model == COMPACT_MODEL || model == WIDE_MODEL) {
            int rowNum = startRowNum;
            for (RowTableTitle rowTableTitle : this.rowTableTitles) {
                int cellNum = startCellNum;
                for (CellTableTitle cellTableTitle : rowTableTitle) {
                    cellTableTitle.setSize(cellTableTitle.getWidth(), model == COMPACT_MODEL ? rowTableTitle.getAvgHeight() : rowTableTitle.getMaxHeight());
                    cellTableTitle.setPosition(rowNum, cellNum);
                    cellNum += cellTableTitle.getWidth();
                }
                rowNum += rowTableTitle.get(0).getHeight();
            }
            return;
        }
        if (model == FIXED_MOVE_RIGHT) {
            List<Rectangle> rectangleList = new ArrayList<>();
            int rowNum = startRowNum;
            int firstAllWidth = rowTableTitles[0].getAllWidth();
            for (RowTableTitle rowTableTitle : this.rowTableTitles) {
                int cellNum = startCellNum;
                boolean isFirstCell = true;
                for (CellTableTitle cellTableTitle : rowTableTitle) {
                    Rectangle position = findPosition(rectangleList, cellTableTitle, rowNum, cellNum);
                    while (isFirstCell) {
                        if (checkFirstCellPosition(startCellNum, firstAllWidth, rowTableTitle, position)) {
                            isFirstCell = false;
                            break;
                        } else {
                            rowNum++;
                        }
                        position = findPosition(rectangleList, cellTableTitle, rowNum, cellNum);
                    }
                    cellTableTitle.setPosition((int) position.getY(), (int) position.getX());
                    cellNum = (int) (position.getX() + position.getWidth());
                    rectangleList.add(position);
                }
                rowNum += rowTableTitle.getMinHeight();
            }
        }
    }

    // 主要检查 rowNum 是否合理 (当前行的最末位置 不能超出首行的 30% 除非 当前行已经重起始列开始了)
    private boolean checkFirstCellPosition(int firstStartCellNum, int firstAllWidth, RowTableTitle rowTableTitle, Rectangle firstCellRectangle) {
        return (firstCellRectangle.getX() - firstStartCellNum + rowTableTitle.getAllWidth() - firstAllWidth) / firstAllWidth < 0.3 || firstStartCellNum == firstCellRectangle.getX();
    }

    /**
     * // 查找目前表格合理位置  （可以做优化 去掉不可能再重叠的表格信息）
     *
     * @param rectangleList  已有表格位置 （可以去掉不可能再重叠的表格信息）
     * @param cellTableTitle 当前表格配置信息
     * @param startRowNum    开始行 (当前行)
     * @param startCellNum   开始列 (当前可能的列)
     * @return {@link Rectangle}
     * @author 12614
     * @date 2020-05-27 23:19
     */
    private Rectangle findPosition(List<Rectangle> rectangleList, CellTableTitle cellTableTitle, int startRowNum, int startCellNum) {
        Rectangle rectangle1 = new Rectangle(startCellNum, startRowNum, cellTableTitle.getWidth(), cellTableTitle.getHeight());
        boolean intersects = true;
        a:
        while (intersects) {
            for (Rectangle rectangle : rectangleList) {
                if (rectangle.intersects(rectangle1)) {
                    double x = rectangle.getX();
                    double x2 = x + rectangle.getWidth();
                    rectangle1.setLocation((int) x2, (int) rectangle1.getY());
                    continue a;
//                        double r1x = rectangle1.getX();
//                        if (r1x > x && r1x < x2) { // 顶点在 里面
//                            rectangle1.setLocation((int) Math.ceil(r1x + (x2 - r1x)), (int) rectangle1.getY());
//                        } else { // 右上 顶点 在内
////                            double r1x2 = r1x + rectangle1.getWidth();
//
//                            rectangle1.setLocation((int) Math.ceil(r1x + (x2 - r1x)), (int) rectangle1.getY());
//                        }

                }
            }
            intersects = false;
        }
        return rectangle1;
    }


    /**
     * // 解析设置表头 并计算最末行表头（排序后的 用来取数据）
     *
     * @param sheet
     * @author 12614
     * @date 2020-05-25 23:50
     */
    public List<CellTableTitle> builder(Sheet sheet) {
        calculation();
        // 解析表头
        for (RowTableTitle rowTitle : this.rowTableTitles) {
            Row row = sheet.createRow(rowTitle.get(0).getX());
            for (CellTableTitle cellTitle : rowTitle) {
                CellRangeAddress position = cellTitle.getCellRangeAddress();
                if (position.getFirstRow() != position.getLastRow() || position.getFirstColumn() != position.getLastColumn()) {
                    sheet.addMergedRegion(position);
                }
                int firstColumn = position.getFirstColumn();
                Cell cell = row.createCell(firstColumn);
                cell.setCellValue(cellTitle.getTitle());
            }
        }
        // 设置行 样式 高度  (一定要等表头初始化完成  这里涉及到列关联，没初始化的表头获取不到会报空)
        for (RowTableTitle rowTitle : this.rowTableTitles) {
            int currentRowIndex = rowTitle.get(0).getX();
            Row row = sheet.getRow(rowTitle.get(0).getX());
            setRowTitleStyle(row, rowTitle);
            for (CellTableTitle cellTitle : rowTitle) {
                setRowStyleHeight(sheet, currentRowIndex, rowTitle, cellTitle);
                // 设置单个表格样式
                Cell cell = row.getCell(cellTitle.getY());
                setCellStyleWidth(cell, rowTitle, cellTitle);
                setCellTitleStyle(cell, cellTitle);
//                CellUtil.setAlignment(cell, HorizontalAlignment.CENTER_SELECTION);
            }
        }

        // 计算最末行表头
        List<CellTableTitle> lastRowTableTitle = new ArrayList<>();
        int maxRowNum = 0;
        for (RowTableTitle rowTitles : this.rowTableTitles) {
            CellTableTitle cellTableTitle = rowTitles.get(rowTitles.size() - 1);
            maxRowNum = Math.max(maxRowNum, cellTableTitle.getY() + cellTableTitle.getWidth() + 1);
        }
        Rectangle lastRectangle = new Rectangle(startCellNum, this.rowTableTitles[this.rowTableTitles.length - 1].get(0).getX(), maxRowNum - startCellNum, 1);
        for (RowTableTitle rowTitles : this.rowTableTitles) {
            for (CellTableTitle title : rowTitles) {
                if (lastRectangle.intersects(new Rectangle(title.getY(), title.getX(), title.getWidth(), title.getHeight()))) {
                    lastRowTableTitle.add(new CellTableTitle(title.getTitle(), title.getFieldName(), title.getPosition(), title.getWidth(), title.getHeight()));
                }
            }
        }
        lastRowTableTitle.sort((Comparator.comparingInt(CellTableTitle::getY)));
        return lastRowTableTitle;
    }

    //    Map<MyCellStyle, CellStyle> cellStyleCacheMap = new HashMap<>();
    // 保存每一个表格(位置) 样式
//    Map<String, MyCellStyle> allCellStyleMap = new HashMap<>();

    private void setRowTitleStyle(Row row, RowTableTitle rowTitle) {
        MyCellStyle aStyle = getCellStyle(), rStyle = rowTitle.getCellStyle(), nStyle = null;
        if (aStyle != null && rStyle != null) {
            nStyle = aStyle == rStyle ? aStyle : new MyCellStyle(aStyle).merge(rStyle);
        } else if (aStyle != null) {
            nStyle = aStyle;
        } else if (rStyle != null) {
            nStyle = rStyle;
        }
        if (nStyle != null) {
//            CellStyle cellStyle = cellStyleCacheMap.computeIfAbsent(nStyle, e -> row.getSheet().getWorkbook().createCellStyle());
//            nStyle.initCellStyle(cellStyle);
//            row.setRowStyle(cellStyle); 有问题
            for (CellTableTitle cellTableTitle : rowTitle) {
                MyCellUtil.setCellStyle(row.getCell(cellTableTitle.getY()), nStyle);
//                row.getCell(cellTableTitle.getY()).setCellStyle(this.cellStyle);
//                allCellStyleMap.put(cellTableTitle.getPosition().toString(), nStyle);
            }
        }
    }

    private void setCellTitleStyle(Cell cell, CellTableTitle cellTitle) {
        MyCellStyle cStyle = cellTitle.getCellStyle();
        if (cStyle != null) {
//            int height = cellTitle.getHeight();
//            int width = cellTitle.getWidth();
//            Point startPoint = cellTitle.getPosition();
//            for (int i = 0; i < width; i++) {
//                for (int j = 0; j < height; j++) {
//            int i = 0, j = 0;
//            String key = new Point((int) startPoint.getX() + i, (int) startPoint.getY() + j).toString();
            String key = cellTitle.getPosition().toString();
//            MyCellStyle nStyle = cStyle;
//            if (allCellStyleMap.containsKey(key)) {
//                MyCellStyle oldStyle = allCellStyleMap.get(key);
//                if (oldStyle == cStyle) return;
//                nStyle = new MyCellStyle(oldStyle).merge(cStyle);
//            }
//            CellStyle cellStyle = cellStyleCacheMap.computeIfAbsent(nStyle, e -> cell.getSheet().getWorkbook().createCellStyle());
//            cell.setCellStyle(nStyle.initCellStyle(cellStyle));
            MyCellUtil.setCellStyle(cell, cStyle);
//            allCellStyleMap.put(cellTitle.getPosition().toString(), nStyle);
//                }
//            }
        }

    }

    private final Map<String, Integer> widthOrHeightWeightMap = new HashMap<>(); // 保存表格宽度或者高度的 权重

    private void setCellStyleWidth(Cell cell, RowTableTitle rowTitle, CellTableTitle cellTitle) {
        int columnIndex = cell.getColumnIndex();
        Sheet sheet = cell.getSheet();
        int aW = CommonUtils.getObjectAllocateFunctionVal(this, "getCellSize", MyCellUtil.EMPTY_CELL_STYLE_SIZE).getWidth(),
                rW = CommonUtils.getObjectAllocateFunctionVal(rowTitle, "getCellSize", MyCellUtil.EMPTY_CELL_STYLE_SIZE).getWidth(),
                cW = CommonUtils.getObjectAllocateFunctionVal(cellTitle, "getCellSize", MyCellUtil.EMPTY_CELL_STYLE_SIZE).getWidth();
        int w = cW != -1 ? cW : rW != -1 ? rW : aW, weight = cW != -1 ? 2 : rW != -1 ? 1 : 0;
        if (w != -1) {
            int width = cellTitle.getWidth();
            for (int i = 0; i < width; i++) {
                boolean isUpdate = true;
                String key = columnIndex + i + " WIDTH";
                if (widthOrHeightWeightMap.containsKey(key)) {
                    if (widthOrHeightWeightMap.get(key) > weight) {
                        isUpdate = false;
                    }
                }
                if (isUpdate) {
                    sheet.setColumnWidth(columnIndex + i, w * 256 + 245);
                    widthOrHeightWeightMap.put(key, weight);
                }
            }
        }

    }


    private void setRowStyleHeight(Sheet sheet, int currentRowIndex, RowTableTitle rowTitle, CellTableTitle cellTitle) {
        short aH = CommonUtils.getObjectAllocateFunctionVal(this, "getCellSize", MyCellUtil.EMPTY_CELL_STYLE_SIZE).getHeight(),
                rH = CommonUtils.getObjectAllocateFunctionVal(rowTitle, "getCellSize", MyCellUtil.EMPTY_CELL_STYLE_SIZE).getHeight(),
                cH = CommonUtils.getObjectAllocateFunctionVal(cellTitle, "getCellSize", MyCellUtil.EMPTY_CELL_STYLE_SIZE).getHeight();
        short h = cH != -1 ? cH : rH != -1 ? rH : aH;
        int weight = cH != -1 ? 2 : rH != -1 ? 1 : 0;
        if (h != -1) {
            int height = cellTitle.getHeight(); // 占块大小
            for (int i = 0; i < height; i++) {
                Row row = sheet.getRow(currentRowIndex + i);
                boolean isUpdate = true;
                String key = currentRowIndex + i + " HEIGHT";
                if (widthOrHeightWeightMap.containsKey(key)) {
                    Integer oldWeight = widthOrHeightWeightMap.get(key);
                    if (oldWeight > weight) {
                        isUpdate = false;
                    } else if (oldWeight == weight && row.getHeight() >= h) {
                        isUpdate = false;
                    }
                }
                if (isUpdate) {
                    row.setHeight((short) (h * 22));
                    widthOrHeightWeightMap.put(key, weight);
                }
            }
        }
    }


    @Override
    public Iterator<RowTableTitle> iterator() {
        return new Iterator<RowTableTitle>() {
            final int currentVersion = version;
            int cursor = 0;

            @Override
            public boolean hasNext() {
                return rowTableTitles != null && cursor != rowTableTitles.length;
            }

            @Override
            public RowTableTitle next() {
                if (rowTableTitles == null) {
                    return null;
                }
                if (currentVersion != version) {
                    throw new ConcurrentModificationException();
                }
                return rowTableTitles[cursor++];
            }
        };
    }
}
